<!DOCTYPE html>
<html lang="en">
<head>
    <title>System TTS Forwarder</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.staticfile.org/bootstrap/5.2.3/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.staticfile.org/jquery/3.6.3/jquery.min.js"></script>
</head>
<body>
<nav>
    <a class="navbar-brand github-button" href="https://github.com/jing332/tts-server-android" data-size="large"
       data-show-count="true" aria-label="前往Github项目 / Star project on GitHub">tts-server-android</a>
</nav>
<div class="container" style="margin-top: 10px;">
    <div class="row">
        <div class="col">
            <label id="label_engine" for="select_engine"> TTS引擎 (Engine)</label>
            <select class="form-select" id="select_engine" onchange="engineChanged()">
                <option value="">加载中/Loading</option>
            </select>
        </div>
    </div>


    <div class="form-check form-switch">
        <input class="form-check-input" type="checkbox" id="switch_custom" onchange="switchCustomChanged()">
        <label class="form-check-label" for="switch_custom">自定义 (Custom)</label>
    </div>

    <div class="collapse" id="customVoiceCollapse">
        <div class="card card-body">
            <div class="col">
                <label id="label_locale" for="select_locale"> 地区 (Locale)</label>
                <select class="form-select" id="select_locale" onchange="localeChanged()">
                    <option value="">加载中/Loading</option>
                </select>
            </div>

            <div class="row">
                <div class="col">
                    <label id="label_voice" for="select_voice"> 声音 (Voice)</label>
                    <select class="form-select" id="select_voice" onchange="voiceChanged()">
                        <option value="">加载中/Loading</option>
                    </select>
                </div>
            </div>
        </div>
    </div>


    <div class="row" style="margin-top: 10px">
        <div class="col">
            <label for="textarea_test"> 测试文本 (Sample text)</label>
            <textarea class="form-control" id="textarea_test" rows="4">如果喜欢这个项目的话请点个Star吧。&#13;If you like this project, please click Star.</textarea>
        </div>
    </div>

    <div class="row" style="margin-top: 10px">
        <div class="col">
            <label>
                语速 (Speed)
                <input id="input_rate" class="form-control" type="number" value="50" style="max-width: 100px"
                       oninput="if(value>100){value=100}else if (value <1){value=1}">
            </label>

            <label style="padding-left: 10px">
                音调 (Pitch)
                <input id="input_pitch" class="form-control" type="number" value="100" max="300" min="1"
                       style="max-width: 100px;"
                       oninput="if(value>300){value=300}else if (value <1){value=1}">
            </label>
        </div>
    </div>

    <div class="row" style="margin-top:10px">
        <div class="col">
            <button class="btn btn-primary" id="btn_test" onclick="onBtnTestClick()">
                <span class="spinner-border spinner-border-sm" id="btn_test_load" hidden="hidden"></span>
                测试 (Test)
            </button>
            <button class="btn btn-success" id="btn_import_legado" onclick="onBtnImportClick()">阅读 Legado</button>
        </div>
    </div>

    <div class="col" style="margin-top: 10px">
        <audio controls="controls" id="audio">
            你的浏览器不支持 &lt;audio&gt; 音频标签。
            Your browser does not support the &lt;audio&gt; tag.
            <source id="source" src="" type="audio/wav"/>
        </audio>
    </div>

</div>


<div id="legadoUrlModal" class="modal fade" aria-hidden="true">
    <div class="modal-dialog modal-dialog-centered">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">阅读链接</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <div style="position: relative">
                    <textarea id="legadoUrl" class="form-control" readonly rows="3"
                              cols="2"></textarea>
                    <button id="copyBtn" class="btn btn-dark btn-sm"
                            style="--bs-btn-font-size: 10px;position:absolute;right:0;top:0;z-index: 20"
                            onclick="copyLegadoUrl()">复制
                    </button>

                </div>
                <div class="alert alert-primary" role="alert">
                    请在阅读的朗读引擎设置中选择网络导入此链接。
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-success" onclick="onBtnOneImportClick()">一键导入</button>
                </div>
            </div>
        </div>
    </div>
</div>
<script src=https://cdn.staticfile.org/github-buttons/2.22.0/buttons.min.js></script>
<script src="https://cdn.staticfile.org/bootstrap/5.2.3/js/bootstrap.bundle.min.js"></script>
<script>
    let engineElement = $("#select_engine")[0]
    let localeElement = $("#select_locale")[0]
    let voiceElement = $("#select_voice")[0]
    let switchElement = $("#switch_custom")[0]
    let collapseElement = $("#customVoiceCollapse")
    let sampleTextElement = $("#textarea_test")[0]

    let storageEngine = localStorage.getItem("engine")
    let storageLocale = localStorage.getItem("locale")
    let storageVoice = localStorage.getItem("voice")
    let storageIsCustom = localStorage.getItem("isCustom")
    let storageSampleText = localStorage.getItem("sampleText")

    function switchCustomChanged() {
        let visible = "hide"
        if (switchElement.checked) visible = "show"
        collapseElement.collapse(visible)
    }

    window.onload = function () {
        if (storageIsCustom) {
            switchElement.checked = storageIsCustom
            switchCustomChanged()
        }

        sampleTextElement.value = storageSampleText.trim().length === 0 ? "如果喜欢这个项目的话请点个Star吧。\nIf you like this project, please click Star." : storageSampleText
    }

    window.onbeforeunload = function () {
        localStorage.setItem("sampleText", sampleTextElement.value)
    }

    function engineChanged() {
        localStorage.setItem("engine", engineElement.value)
        fetchVoices()
    }

    function localeChanged() {
        localStorage.setItem("locale", localeElement.value)
        updateVoice()
    }

    function voiceChanged() {
        localStorage.setItem("voice", voiceElement.value)
    }

    let baseUrl = ''
    // let baseUrl = 'http://192.168.0.106:1233'

    let engines = {}
    fetch(baseUrl + '/api/engines', {mode: 'cors'})
        .then(response => {
            if (response.status === 200) {
                return response.json()
            } else {
                return response.text().then(text => Promise.reject(text))
            }
        })
        .then(data => {
            engines = data
            updateEngine()
        })
        .catch(reason => {
            alert("加载引擎数据失败\n" +
                "Failed to load engine data: \n" +
                reason)
        })

    /* 复制导入链接 */
    function copyLegadoUrl() {
        let copyBtn = $('#copyBtn')[0]
        copyBtn.disabled = true
        setTimeout(function () {
            copyBtn.innerText = "复制"
            copyBtn.disabled = false
        }, 2000)

        try {
            document.getElementById("legadoUrl").select()
            let ok = document.execCommand("copy");
            if (ok) copyBtn.innerText = "已复制到剪贴板"
            else copyBtn.innerText = "复制失败 请手动复制"
        } catch (e) {
            copyBtn.innerText = "复制失败 请手动复制"
        }
    }

    function onBtnOneImportClick() {
        window.location.href = 'legado://import/httpTTS?src=' + $('#legadoUrl')[0].value
    }

    function onBtnImportClick() {
        let selectEngine = $('#select_engine')[0]
        let engine = selectEngine.value
        let name = getSelectedText(selectEngine)
        let pitch  = $('#input_pitch')[0].value

        let url = `${window.location.protocol}//${window.location.host}/api/legado?api=${encodeURI(window.location.protocol)}//${window.location.host}/api/tts`
            + `&name=${name}&engine=${engine}&pitch=${pitch}`
        if (switchElement.checked) url += `&voice=${voiceElement.value}`

        $('#legadoUrl')[0].value = url
        let modal = new bootstrap.Modal($('#legadoUrlModal')[0])
        modal.show()
    }

    function getSelectedText(obj) {
        let index = obj.selectedIndex
        return obj.options[index].text
    }

    function getSelectedOption(obj) {
        return obj.options[obj.selectedIndex]
    }

    function onBtnTestClick() {
        let speechRate = $('#input_rate')[0].value
        let pitch = $('#input_pitch')[0].value
        let engine = $('#select_engine')[0].value
        let voice = $('#select_voice')[0].value
        let isCustom = switchElement.checked

        let btn = $("#btn_test")[0]
        let load = $('#btn_test_load')[0]
        btn.disabled = true
        load.hidden = false

        let text = $('#textarea_test')[0].value
        let params = `/api/tts?text=${text}&engine=${engine}&rate=${speechRate}&pitch=${pitch}`
        if (isCustom) {
            params += `&voice=${voice}`
        }
        fetch(baseUrl + params,
            {method: 'GET'})
            .then(response => {
                if (response.status === 200) {
                    return response.arrayBuffer()
                } else {
                    return response.text().then(text => Promise.reject(text))
                }
            })
            .then(arrayBuffer => {
                let blob = new Blob([arrayBuffer], {type: 'audio/x-wav'})
                let audio = $('#audio')[0]
                audio.src = window.URL.createObjectURL(blob)
                audio.load()
                audio.play()
            })
            .catch(reason => {
                alert(reason)
            })
            .finally(() => {
                btn.disabled = false
                load.hidden = true
            })
    }

    function updateEngine() {
        engineElement.innerHTML = ''
        engineElement.label = $('#label_engine').innerText
        engines.forEach(engine => {
                let name = engine['name']
                let label = engine['label']

                let option = document.createElement('option')
                option.innerText = label + " (" + name + ")"
                option.value = name
                if (storageEngine === name) option.selected = true

                engineElement.append(option)
            }
        )

        fetchVoices()
    }

    let voices = {}

    function fetchVoices() {
        let engine = getSelectedOption(engineElement).value
        fetch(baseUrl + `/api/voices?engine=${engine}`, {mode: 'cors'})
            .then(response => {
                if (response.status === 200) {
                    return response.json()
                } else {
                    return response.text().then(text => Promise.reject(text))
                }
            })
            .then(data => {
                voices = data
                updateLocaleAndVoice()
            })
            .catch(reason => {
                alert("加载语音数据失败\n" +
                    "Failed to load voice data: \n" +
                    reason)
            })
    }

    function updateLocaleAndVoice() {
        let locales = new Map()
        voices.forEach(voice => {
            let locale = voice['locale']
            locales.set(locale, voice['localeName'])
        })

        const sortMap = new Map([...locales.entries()].sort((a, b) => a[1].localeCompare(b[1])));
        localeElement.innerHTML = ''
        sortMap.forEach((value, key) => {
            let option = document.createElement('option')
            option.innerText = value
            option.value = key
            if (storageLocale === key) option.selected = true

            localeElement.append(option)
        })

        updateVoice()
    }

    function updateVoice() {
        voiceElement.innerHTML = ''
        voiceElement.label = $('#label_voice').innerText
        let currentLocale = getSelectedOption(localeElement).value
        voices.forEach(voice => {
            let locale = voice['locale']
            if (currentLocale === locale) {
                let name = voice['name']
                let features = voice['features']
                let option = document.createElement('option')
                if (features) {
                    option.innerText = name + ` (${features})`
                } else {
                    option.innerText = name
                }
                if (storageVoice === name) option.selected = true

                option.value = name
                voiceElement.append(option)
            }
        })

    }


</script>
</body>
</html>